﻿namespace PES.Common.Extensions
{
    using System;
    using System.Collections.Generic;
    using System.Data;

    /// <summary>
    /// Classe responsável por gerênciar os mapeamentos entre os tipos CLR e DbType.
    /// </summary>
    internal class DbTypeMapper
    {
        private static readonly Dictionary<Type, DbType> s_dbMapper;

        static DbTypeMapper()
        {
            // Cria a tabela que mapeia os tipos CLR para os DbType equivalentes.
            s_dbMapper = new Dictionary<Type, DbType>();
            s_dbMapper.Add(typeof(sbyte), DbType.SByte);
            s_dbMapper.Add(typeof(byte), DbType.Byte);
            s_dbMapper.Add(typeof(short), DbType.Int16);
            s_dbMapper.Add(typeof(ushort), DbType.UInt16);
            s_dbMapper.Add(typeof(int), DbType.Int32);
            s_dbMapper.Add(typeof(uint), DbType.UInt32);
            s_dbMapper.Add(typeof(long), DbType.Int64);
            s_dbMapper.Add(typeof(ulong), DbType.UInt64);
            s_dbMapper.Add(typeof(byte[]), DbType.Binary);

            s_dbMapper.Add(typeof(string), DbType.String);
            s_dbMapper.Add(typeof(bool), DbType.Boolean);

            s_dbMapper.Add(typeof(float), DbType.Single);
            s_dbMapper.Add(typeof(double), DbType.Double);
            s_dbMapper.Add(typeof(decimal), DbType.Decimal);

            s_dbMapper.Add(typeof(Guid), DbType.Guid);
            s_dbMapper.Add(typeof(DateTime), DbType.DateTime);
        }

        /// <summary>
        /// Recupera o tipo equivalente ao especificado na base de dados.
        /// </summary>
        /// <typeparam name="T">Tipo a ser mapeado.</typeparam>
        /// <returns>Tipo equivalente no banco.</returns>
        internal static DbType GetDbEquivalent<T>()
        {
            return s_dbMapper[GetUnderlyingType<T>()];
        }

        public static Type GetUnderlyingType<T>()
        {
            Type t = typeof(T);
            // Se for um enum, retorna o tipo base do enum.
            if (t.IsEnum) return Enum.GetUnderlyingType(typeof(T));

            // Se for por valor, pode ainda ser um nullable.
            if (t.IsValueType) return Nullable.GetUnderlyingType(typeof(T)) ?? t;

            return t;
        }
    }
}
